#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<getopt.h>
#include<signal.h>
#include<net/if.h>
#include<proto.h>
#include "client.h"
#include<string.h>

#define BUFSIZE		(1024*1024)


/*
-M --mgroup 指定多播组
-P --port 	指定接收端口
-p --player	指定播放器
-H --help	显示帮助

*/

struct client_conf_st client_conf={\
							.rcvport=DEFAULT_RCVPORT,\
							.mgroup=DEFAULT_MGROUP,\
							.player_cmd=DEFAULT_PLAYERCMD};

static struct msg_channel_st *msg_channel;
static int sd;
static void printhelp()
{
	printf("-M --mgroup  指定多播组\n");
	printf("-P --port	 指定接收端口\n");
	printf("-p -player	 指定播放器\n");
	printf("-H --help	 显示帮助\n");
}

static void func(int s)
{
	free(msg_channel);
	close(sd);
	exit(0);
}
static ssize_t writen(int fd,const char *buf,int len)
{
	int pos=0;
	int ret;
	while(len>0)
	{
		ret=write(fd,buf,len);
		if(ret<0)
		{
			if(errno == EINTR)
				continue;
			perror("write()");
			return -1;
		}
		pos+=ret;
		len-=ret;
	}

	return pos;
}

int main(int argc,char **argv)
{
	
	char buffer[BUFSIZE];
	int buffer_offset=0;

	int c,index,val,len,chosenid,ret;
	int pd[2];
	pid_t pid;
	struct sigaction sa;
	struct ip_mreqn mreq;
	struct sockaddr_in laddr,serveraddr,raddr;
	socklen_t raddr_len,serveraddr_len;
	struct option argarr[]={{"port",1,NULL,'P'},{"mgroup",1,NULL,'M'},\
							{"player",1,NULL,'p'},{"help",0,NULL,'H'},\
							{NULL,0,NULL,0}};
	/*初始化
	级别：默认值，配置文件，环境变量，命令行参数
	*/
	while(1)
	{
		c=getopt_long(argc,argv,"P:M:p:H",argarr,&index);
		if(c<0) //所有命令解析完之后没有找到,返回-1
			break;

		switch(c)
		{
			case 'P':
				client_conf.rcvport=optarg;
				break;
			case 'M':
				client_conf.mgroup=optarg;
				break;
			case 'p':
				client_conf.player_cmd=optarg;
				break;
			case 'H':
				printhelp();
				break;
			default:
				abort();
				break;
		}	
	}
	sa.sa_handler=func;
	sigemptyset(&sa.sa_mask);
	sigaddset(&sa.sa_mask,SIGINT);
	sigaddset(&sa.sa_mask,SIGTERM);
	sigaddset(&sa.sa_mask,SIGQUIT);

	sigaction(SIGINT,&sa,NULL);
	sigaction(SIGTERM,&sa,NULL);
	sigaction(SIGQUIT,&sa,NULL);
	
	sd=socket(AF_INET,SOCK_DGRAM,0);
	if(sd<0)
	{
		perror("socket");
		exit(1);
	}
	//将点分式转换成大整数	
	inet_pton(AF_INET,client_conf.mgroup,&mreq.imr_multiaddr);
	inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);
	mreq.imr_ifindex=if_nametoindex("ens33");

	if(setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0)
	{
		perror("setsockopt()");
		exit(1);
	}
	val=1;
	if(setsockopt(sd,IPPROTO_IP,IP_MULTICAST_LOOP,&val,sizeof(val))<0)
	{
		perror("setsockopt()");
		exit(1);
	}
	
	laddr.sin_family=AF_INET;
	laddr.sin_port=htons(atoi(DEFAULT_RCVPORT));
	inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);

	if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
	{
		perror("bind()");
		exit(1);
	}

	if(pipe(pd)<0)
	{
		perror("pipe()");
		exit(1);
	}

	pid=fork();
	if(pid<0)
	{
		perror("fork()");
		exit(1);
	}
	
	if(pid==0)  //子进程
	{
		//子进程，调用解码器
		close(pd[1]);
		close(sd);
		dup2(pd[0],0);
		if(pd[0]>0)
			close(pd[0]);
		execl("/bin/sh","sh","-c",client_conf.player_cmd,NULL);
		//重定向需要使用sh -c来解析
		//execlp("sh","sh","-c",..,NULL)
		perror("execl()");
		exit(0);
	}
	
	close(pd[0]);
	struct msg_list_st *msg_list;
	msg_list=malloc(MSG_LIST_MAX);
	if(msg_list==NULL)
	{
		perror("malloc()");
		exit(1);
	}
	serveraddr_len=sizeof(serveraddr);
	//parent 从网络上收包，发送给子进程
	//收节目单
	while(1)
	{
		
		len=recvfrom(sd,msg_list,MSG_LIST_MAX,0,(void *)&serveraddr,&serveraddr_len);
		if(len<sizeof(struct msg_list_st))
		{
			fprintf(stderr,"msg is too small\n");
			continue;
		}	

		if(msg_list->chnid!=LISTCHNID)
		{
			fprintf(stderr,"chnid is not match.\n");
			continue;
		}else
			break;
	}
	struct msg_listentry_st *pos;
	//打印节目单，并选择频道
	for(pos=msg_list->entry;(char *)pos<(((char *)msg_list)+len);pos=(void *)(((char *)pos)+ntohs(pos->len)))
	{
		printf("channel %d : %s\n",pos->chnid,pos->desc);
	}
	
	free(msg_list);
	
	//选择频道
	while(1)
	{
		ret=scanf("%d",&chosenid);
		if(ret==1) break;
		if(ret!=1)  //输入其他字符直接退出
			exit(1);
	}


	//收频道包，发送给子进程
	
	msg_channel=malloc(MSG_CHANNEL_MAX);
	if(msg_channel==NULL)
	{
		perror("malloc()");
		exit(1);
	}
	
	raddr_len=sizeof(raddr);
	while(1)
	{
		len=recvfrom(sd,msg_channel,MSG_CHANNEL_MAX,0,(void *)&raddr,&raddr_len);

		if(raddr.sin_addr.s_addr!=serveraddr.sin_addr.s_addr || raddr.sin_port!=serveraddr.sin_port)
		{
			fprintf(stderr,"Ignore:address not math.\n");
			continue;
		}
		if(len<sizeof(struct msg_channel_st))
		{
			fprintf(stderr,"Ignore:message too small.\n");
			continue;
		}
		
		if(msg_channel->chnid==chosenid)
		{
			fprintf(stdout,"accepted msg:%d recieved.\n",msg_channel->chnid);
			int data_len=len-sizeof(chnid_t);

			if(buffer_offset+data_len>BUFSIZE)
			{
				if(writen(pd[1],buffer,buffer_offset)<0)
					exit(1);

				buffer_offset=0;
			}
			
			memcpy(buffer+buffer_offset,msg_channel->data,data_len);
			buffer_offset+=data_len;
			if(buffer_offset>=BUFSIZE/8)
			{
				if(writen(pd[1],buffer,buffer_offset)<0)
				{
					exit(1);
				}

				buffer_offset=0;
			}
		}
	}

	exit(0);
}



